import shutil
from loguru import logger
import numpy as np
from astropy.stats import sigma_clip
from astropy.io import fits
import matplotlib.pyplot as plt
from tools import PSFPhotometry, utils
import os
from tools.objectlist import crdList
from tools.util_statck import calculate_limiting_magnitude, calculate_mid_exposure_time, crossmatch, todegree
from datetime import datetime
from astropy.table import Table  # 用于操作星表（FITS 表格等）
from tools.utltil_mosic import get_log_file_path_ospath

if __name__ == "__main__":
    # -------------------------------
    # 1. 基础配置
    # -------------------------------
    targetName = "01709133918" 
    DETECT_MINAREA = 3
    DETECT_THRESH = 0.5
    ANALYSIS_THRESH = 0.5
    # 目标名称，用于后续路径和文件命名

    limitmaglist = []
    # 用于存储在不同滤光片下计算到的极限星等，便于在处理结束后统一查看或统计

    # 定义两个需要写入的结果文件：
    #   1) target_name：主要保存最终的测光结果，如极限星等、曝光时间等
    #   2) target_name_zp：主要保存光度归一化时参考图像、相对光度比等零点信息
    maindir = "."
    reuslt_dir = "result/" + targetName
    utils.ensure_directories(reuslt_dir)

    imgdir = os.path.join(maindir, "images", targetName)
    products_dir = os.path.join(maindir, "products", targetName)
    confdir = os.path.join(maindir, "config")
    figdir = os.path.join(maindir, "figures", targetName)
    # 若目标目录不存在，自动创建
    utils.ensure_directories(products_dir)
    utils.ensure_directories(figdir)

    date_list = {}
    # 用于记录每个滤光片对应的图像拍摄日期，可能在后续用于校验观测时段等
    for filterName in ['u', 'v', 'g', 'r', 'i', 'z']:
        # 使用 with 语句在“追加模式”下 (a) 同时打开两个文件
        target_name_zp = os.path.join(reuslt_dir, targetName + "_" + filterName + "_zpinfo.txt")
        target_name_single = os.path.join(reuslt_dir, targetName + "_" + filterName + "_fullimg_single.csv")

        with open(target_name_zp, 'a', encoding='utf-8') as zp_file, open(target_name_single, 'a', encoding='utf-8') as single_file:
            # 在结果文件 (target_name) 中先写入表头信息
            single_file.write("Fits_Name,Start_Time,Mid-Time,Band,Exposure,CalMAG,MAG_ERROR,FLUX,FLUX_ERR,Phot_TYPE,LMAG_3_Manual,LMAG_3\n")
            
            # 依次处理 6 个滤光片：u, v, g, r, i, z
            force_phot_file_ref = None
            # 日志文件夹，存放日志文件
            log_dir = os.path.join("logs", targetName)
            os.makedirs(log_dir, exist_ok=True)  # 如果 logs 文件夹不存在则创建
            
            # 使用 Loguru 配置日志文件，指定大小轮转、日志级别等
            log_file_path = get_log_file_path_ospath(log_dir, targetName, filterName, type="stack")
            logger.add(log_file_path, rotation="1 MB", level="DEBUG", backtrace=True, diagnose=True)

            logger.info(f"##################### starting {targetName} and {filterName} #####################")
            crdTar = crdList[targetName]
            # 根据滤光片类型，设置不同的像素分辨率 (ipixScale)
            if filterName in ['u', 'v', 'g', 'r']:
                ipixScale = 0.429
                crdTar[2] = 6160
                crdTar[3] = 6144
            elif filterName in ['i', 'z']:
                ipixScale = 0.286
                crdTar[2] = 9232
                crdTar[3] = 9216

            # SExtractor 的配置与参数文件
            sexParam = os.path.join(confdir, "default.param") 
            sexConf = os.path.join(confdir, "default_limit.sex")  # 该地方使用的ORI参数，该参数中的DEBLEND_MINCONT设置的较大，不至于识别到许多特别小的源

            # SExtractor 的命令行模板
            sexComd1 = "sex %s -c %s -PARAMETERS_NAME %s -CATALOG_NAME %s "
            sexComd2 = "-BACK_SIZE 64  -DETECT_MINAREA %s -DETECT_THRESH %s -ANALYSIS_THRESH %s "
            sexComdS1 = sexComd1 + sexComd2

            # 读取当前滤光片下的图像列表文件：images_{filterName}.list
            imgListName = os.path.join(imgdir, f"images_{filterName}.list")
            imgList = open(imgListName, "r").read().splitlines()
            nimg = len(imgList)
            logger.info(f"^_^ Total {nimg} {filterName}-band images")

            newImgListName = os.path.join(products_dir, f"imagesDir_{filterName}.list")
            f_newImgList = open(newImgListName, "w")
            # fwhmList：记录每幅图像的 FWHM 用于后续选择参考图像
            fwhmList = {}
            
            # 用于记录日期、曝光时间和中点时间
            date_ = []
            exposure_value_ = []
            mid_times = []
            
            # 读取每幅图像头中的曝光时间(EXPOSURE)和拍摄时间(DATE)，并计算中点时间
            for i in range(nimg):
                iimgX = imgList[i]
                iimg = os.path.join(imgdir, iimgX)
                
                # 将数据拷贝到产品目录
                shutil.copy(iimg, products_dir)
                f_newImgList.write(os.path.join(products_dir, iimgX) + "\n")
                
            # 将该滤光片的日期列表存入字典
            date_list[filterName] = date_
            f_newImgList.flush()
            
            for single_sci in imgList:
                iimgX = os.path.join(products_dir, single_sci)
                icatX = single_sci[:-4] + "ldac"
                icat = os.path.join(products_dir, icatX)
                isex = sexComdS1 % (iimgX, sexConf, sexParam, icat, DETECT_MINAREA, DETECT_THRESH, ANALYSIS_THRESH)
                logger.info(isex)
                os.system(isex)
                
                catalog_data = fits.getdata(icat, ext=2)
                
                ra_dec = todegree(crdTar[0], crdTar[1])
                matched_star = utils.TargetXCatalogue(ra_dec[0], ra_dec[1], catalog_data, radius=2)
                
                # 通过目标坐标，确定参考图像上对应的 Gate 分区，继而从头部读出对应的零点 (ZPMAG_G)
                (x, y), (height, width) = utils.ra_dec_to_xy_and_size(iimgX, crdTar[0], crdTar[1])
                partitioner = utils.MatrixPartitioner(height, width)
                logger.info(f"MatrixPartitioner: {partitioner}")
                
                range_info = partitioner.find_submatrix_with_range(x, y)
                if not range_info:
                    continue
                
                # 从原图像头部读取关键字，如曝光时间(EXPOSURE)、零点(ZPMAG) 等
                v_list = utils.read_header_keys(iimgX, ["LMAG_3_M","EXPOSURE", "ZPMAG", "DATE", "ZPMAG_G"])
                gate_find = range_info['submatrix_index'] - 1
                gate_zpmag_find = float(v_list['ZPMAG_G'].split(',')[gate_find])
                mid_exposure_time = calculate_mid_exposure_time(v_list["DATE"], v_list["EXPOSURE"])
                mag_zp = gate_zpmag_find
                
                if matched_star:  # 如果给定的位置是精确的话，但一般应该都不够精准
                    if not force_phot_file_ref:
                        force_phot_file_ref = iimgX
                        
                    # convert_to_unit_time_magnitude 用于将测得的 MAG_AUTO 转换成单位时间星等
                    m_per_sec, sigma_m_per_sec = utils.convert_to_unit_time_magnitude(
                        matched_star['MAG_AUTO'],
                        v_list["EXPOSURE"],
                        sigma_m=matched_star['MAGERR_AUTO']
                    )
                    # 进一步校正，将 Gate 零点加到单位时间星等上，得到最终校正星等
                    CALMAG = mag_zp + m_per_sec
                    
                    # 计算单位时间星等
                    flux_ = matched_star["FLUX_AUTO"]
                    flux_S_PAN = -2.5 * np.log10(flux_ / v_list["EXPOSURE"])
                    
                    logger.info(f"mag_zp: {mag_zp}, m_per_sec: {m_per_sec:.4f}, CALMAG: {CALMAG}, flux_s: {flux_S_PAN}  星等误差: {sigma_m_per_sec:.4f}")
                    single_file.write(f'''{single_sci},{v_list["DATE"]},{mid_exposure_time},{filterName},{v_list["EXPOSURE"]},{CALMAG},{sigma_m_per_sec},{flux_},{flux_S_PAN},Normal,,{v_list["LMAG_3_M"]}\n''')
                    single_file.flush()
                else:
                    if force_phot_file_ref:
                        sex_config_path = './config/default.sex'
                        sex_config_path_param = './config/default.param'
                        image_force_path = iimgX
                        utils.remove_flxscale_from_fits(image_force_path)
                        
                        # 对齐图像：将当前图像与参考图像做 WCS 坐标系对齐
                        imgoutList = utils.WcsAlignFunc(
                            [force_phot_file_ref, image_force_path],
                            os.path.join("products", targetName, filterName, "wcs_align"),
                            WcsSuffix="_WcsAligned.fits",
                            COMBINE="Y",
                            RESAMPLE="Y"
                        )
                        
                        # 对齐之后，对第二张图像（即当前图像）进行强制测光
                        forced_photometry_path = utils.SexPhotoTwoImg1m6(
                            imgoutList[1],
                            imgoutList[0],
                            None,
                            SexConfig=sex_config_path,
                            SexParam=sex_config_path_param,
                            CataSuffix="_sexcat",
                            PsfPhot=False,
                            DETECT_MINAREA=DETECT_MINAREA,
                            DETECT_THRESH=DETECT_THRESH,
                            ANALYSIS_THRESH=ANALYSIS_THRESH
                        )
                        
                        # 将得到的测光结果用 DS9 区域文件标记
                        utils.CatalogueRegion(forced_photometry_path)
                        
                        # 读取强制测光后的星表（存在 ext=2 中）
                        catalog = Table.read(forced_photometry_path, hdu=2)

                        # 将目标坐标与星表中的天体进行交叉匹配，找到最接近目标的位置
                        matched_star = utils.TargetXCatalogue(ra_dec[0], ra_dec[1], catalog)
                        
                        # 计算单位时间星等
                        flux_ = matched_star["FLUX_AUTO"]
                        flux_S_PAN = -2.5 * np.log10(flux_ / v_list["EXPOSURE"])
                        
                        # 如果成功匹配到天体，则提取 MAG_AUTO 并计算误差
                        if matched_star is not None:
                            logger.info(
                                f"(MAG_AUTO): {matched_star['MAG_AUTO']}, "
                                f"(MAGERR_AUTO): {matched_star['MAGERR_AUTO']}, "
                                f"(ZPMAG): {v_list['ZPMAG']}, "
                                f"ZPMAG_G: {v_list['ZPMAG_G']}, "
                                f"select: {gate_zpmag_find}, "
                                f"gate_find: {gate_find}"
                            )
                            
                            # convert_to_unit_time_magnitude 用于将测得的 MAG_AUTO 转换成单位时间星等
                            m_per_sec, sigma_m_per_sec = utils.convert_to_unit_time_magnitude(
                                matched_star['MAG_AUTO'],
                                v_list["EXPOSURE"],
                                sigma_m=matched_star['MAGERR_AUTO']
                            )
                            
                            # 进一步校正，将 Gate 零点加到单位时间星等上，得到最终校正星等
                            CALMAG = gate_zpmag_find + m_per_sec
                            
                            logger.info(f"星等: {m_per_sec:.4f}, flux_s: {flux_S_PAN}  星等误差: {sigma_m_per_sec:.4f} CALMAG: {CALMAG}")
                            
                            
                            limiting_mag_info = calculate_limiting_magnitude(
                                image_force_path, image_force_path+"_limit.cat", ra_dec, filterName,
                                mag_zp, 10, image_force_path+".png", filterName,ipixScale
                            )
                            
                            # 写入 CSV 文件：日期、文件名、选择的门零点、曝光时间、MAG_AUTO、单位时间星等、校正后的星等、误差
                            single_file.write(f'''{single_sci},{v_list["DATE"]},{mid_exposure_time},{filterName},{v_list["EXPOSURE"]},{CALMAG},{sigma_m_per_sec},{flux_},{flux_S_PAN},Forced,{limiting_mag_info[5]},{v_list["LMAG_3_M"]}\n''')
                            single_file.flush()
                        else:
                            logger.info("No matches found.")
                            # 如果没有匹配到目标，也可将信息记录到 CSV
                            single_file.write(f'''{single_sci},{v_list["DATE"]},{mid_exposure_time},{filterName},'N/A','N/A','N/A','N/A','N/A',ERROR,,{v_list["LMAG_3_M"]}\n''')
                    
                    else:
                        logger.error(f"no ref photometry file")


